﻿using System.AutoUpdate.Core;
using System.AutoUpdate.Core.Operations;
using System.AutoUpdate.Exceptions;
using System.AutoUpdate.Interfaces;
using System.AutoUpdate.Library;
using System.AutoUpdate.Managers;
using System.Collections.Generic;
using System.Linq;

namespace System.AutoUpdate.Operations
{
    /// <summary>
    /// Asynchronus download operation
    /// </summary>
    public class DownloadOperation : BasicOperation
    {
        private List<UpdateTransfer> _downloadQuery;

        /// <summary>
        /// Update info must be sent, because user might not want to download all patches
        /// </summary>
        /// <param name="info"></param>
        public override bool StartOperation (UpdateInfo info)
        {
            //
            // Checks before operation can 
            //

            if (info == null)
            {
                return false;
            }

            Info = info;

            Info.TemporaryDirectory = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
            //
            // Loop through all versions and find appropriate download sources,
            // this compiles a download query that is sent to each source
            //

            _downloadQuery = new List<UpdateTransfer> ();

            foreach (UpdateVersionInfo version in Info.AvailableVersions)
            {
                //
                // Set version download path
                //

                version.File.LocalPath = Info.TemporaryDirectory;

                //
                // Create update transfers
                //

                UpdateTransfer downloadItem = new UpdateTransfer (version);

                UpdateVersionInfo version1 = version;

                foreach (IUpdateSource source in SourceManager.Instance.Sources.Where (source => source.HasVersion (version1)))
                {
                    downloadItem.AvailableSources.Add (source);
                }

                //
                // Sign up to download item events
                //

                downloadItem.TransferProgress += DownloadItemOnTransferProgress;
                downloadItem.TransferCompleted += DownloadItemOnTransferCompleted;

                _downloadQuery.Add (downloadItem);
            }

            OperationEvent(Core.OperationEvent.Started, this, new OperationEventArgs
            {
                UpdateInfo = Info
            });

            //
            // Commence download operation to target directory
            //
 
            foreach (UpdateTransfer downloadItem in _downloadQuery)
            {
                downloadItem.TransferVersion ();
            }

            return true;
        }

        private void DownloadItemOnTransferCompleted (object sender, TransferEventArgs args)
        {
            //
            // Check if all downloaders have finished
            //

            bool isFinished = true;
            bool isSuccess = true;

            foreach (UpdateTransfer transfer in _downloadQuery)
            {
                if (transfer.IsRunning)
                {
                    isFinished = false;
                }

                if (!transfer.Version.File.IsTransfered)
                {
                    isSuccess = false;
                }
            }

            //
            // Send operation completed event if we have finished
            //

            if (isFinished)
            {
                OperationEvent (Core.OperationEvent.Completed, this, new OperationEventArgs
                                                                     {
                                                                         UpdateInfo = Info,
                                                                         Exception = !isSuccess ? new OperationException("Operation failed, couldn't download one of the updates.") : null
                                                                     });
            }

        }

        /// <summary>
        /// A item download is in progress and we received transfer progress event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void DownloadItemOnTransferProgress (object sender, TransferEventArgs args)
        {
            long transferredBytes = 0;

            foreach (UpdateTransfer transfer in _downloadQuery)
            {
                transferredBytes += transfer.TransferredBytes;
            }

            int percentage = (int) ((transferredBytes / (double) Info.UpdateSize) * 100.0);

            OperationEvent (Core.OperationEvent.Progress, this, new OperationEventArgs
                                                                {
                                                                    Progress = percentage
                                                                });
        }
    }
}
